#Anaconda/envs/NetOrchestr python
# -*- coding: utf-8 -*-
'''
simple_minisfc.py
=================

.. module:: simple_minisfc
  :platform: Windows
  :synopsis: 简单场景脚本

.. moduleauthor:: WangXi

简介
----

该脚本构建了一个简单的 SFC 编排与管理场景, 它提供了以下特性:

- 场景包含以下节点:
  - 3 个基站节点 (GroundServerPlatform)
  - 4 个用户节点 (UeWithSfcReq)
- 场景包含以下 SFC: 
  - SFC:0 (UE0->VNF0->VNF1->VNF2->UE1)
  - SFC:1 (UE2->VNF0->VNF1->UE3)
- 支持接入控制器 ControllerMano 进行自动化编排与管理
  - Vnf 管理器 VnfManager
  - SFC 的部署与卸载管理 deploy
  - SFC 的端到端流量管理 e2e
  
仿真完成后会在当前工作空间生成仿真日志与控制器统计结果:

- 仿真器模块级详细日志: SimpleMinisfc.debug.log
- 仿真器节点级简要日志: SimpleMinisfc.log
- 控制器编排策略统计结果: TraceResult_Test.csv
- 控制器网络资源详细日志: TraceNFVI_Test.csv
- 控制器端到端流量统计结果: TraceVNFFG 文件夹
  
版本
----

- 版本 1.0 (2025/09/06): 初始版本

'''

from astropy import units as u
from astropy.time import Time

# Tip: 是否可以通过设置随机数种子使得每次运行的结果相同？（待验证）
# np.random.seed(0)

from netorchestr.envir.base import ONet

net = ONet('SimpleMinisfc')

init_time = Time("2021-07-22 00:00:00")

# region step1: define substrate topologies--------------------------------------------

from netorchestr.envir.node.ground import GroundServerPlatform
from netorchestr.envir.node.ue import UeWithSfcReq
from netorchestr.envir.mobility import Trajectory
from netorchestr.envir.physicallayer import RadioMedium

station_0 = GroundServerPlatform(name='G0', 
                                 init_time=init_time, 
                                 init_gps=[111.80, 36.45, 100],
                                 ip_pool_str='192.168.0.0/24',
                                 node_resource_dict={'cpu':2, 'ram':256*u.MB, 'rom':1*u.GB},
                                 link_resource_dict={'band':100*u.Mbit/u.s})
net.add_module(station_0)

station_1 = GroundServerPlatform(name='G1', 
                                 init_time=init_time, 
                                 init_gps=[116.65, 40.64, 100],
                                 ip_pool_str='192.168.1.0/24',
                                 node_resource_dict={'cpu':4, 'ram':512*u.MB, 'rom':1*u.GB},
                                 link_resource_dict={'band':100*u.Mbit/u.s})
net.add_module(station_1)

station_2 = GroundServerPlatform(name='G2', 
                                 init_time=init_time, 
                                 init_gps=[121.50, 44.83, 100],
                                 ip_pool_str='192.168.2.0/24',
                                 node_resource_dict={'cpu':2, 'ram':256*u.MB, 'rom':1*u.GB},
                                 link_resource_dict={'band':100*u.Mbit/u.s})
net.add_module(station_2)

ue_0 = UeWithSfcReq(name='Ue0', init_time=init_time, init_gps=station_0.mobiusTraj.init_gps)
# ue_0_trajectory = Trajectory(name='U0Tr', avg_speed=1.5)
# ue_0_trajectory.add_random_in_circle(center=ue_0.mobiusTraj.init_gps, radius=(0.001*u.km).to(u.m).value, anchor_number=10)
# ue_0.mobiusTraj.set_trajectory(ue_0_trajectory, init_time)
net.add_module(ue_0)

ue_1 = UeWithSfcReq(name='Ue1', init_time=init_time, init_gps=station_2.mobiusTraj.init_gps)
# ue_1_trajectory = Trajectory(name='U1Tr', avg_speed=1.5)
# ue_1_trajectory.add_random_in_circle(center=ue_1.mobiusTraj.init_gps, radius=(0.001*u.km).to(u.m).value, anchor_number=10)
# ue_1.mobiusTraj.set_trajectory(ue_1_trajectory, init_time)
net.add_module(ue_1)

ue_2 = UeWithSfcReq(name='Ue2', init_time=init_time, init_gps=station_0.mobiusTraj.init_gps)
# ue_2_trajectory = Trajectory(name='U2Tr', avg_speed=1.5)
# ue_2_trajectory.add_random_in_circle(center=ue_2.mobiusTraj.init_gps, radius=(0.001*u.km).to(u.m).value, anchor_number=10)
# ue_2.mobiusTraj.set_trajectory(ue_2_trajectory, init_time)
net.add_module(ue_2)

ue_3 = UeWithSfcReq(name='Ue3', init_time=init_time, init_gps=station_1.mobiusTraj.init_gps)
# ue_3_trajectory = Trajectory(name='U3Tr', avg_speed=1.5)
# ue_3_trajectory.add_random_in_circle(center=ue_3.mobiusTraj.init_gps, radius=(0.001*u.km).to(u.m).value, anchor_number=10)
# ue_3.mobiusTraj.set_trajectory(ue_3_trajectory, init_time)
net.add_module(ue_3)

radio_medium = RadioMedium('RadioMedium')
net.add_module(radio_medium)
net.ready_for_medium(radio_medium)

# endregion

# region step2: define vnf manager--------------------------------------------------

from netorchestr.envir.node.controller.mano.vnfm import VnfManager,VnfEm

vnfManager = VnfManager()
vnfEm_template = VnfEm(type = 'type0',
                       resource_limit = {'cpu':0.2, 
                                         'ram':64*u.MB, 
                                         'rom':128*u.MB},
                       rate_per_core = 10)
vnfManager.add_vnf_into_templates(vnfEm_template)
vnfEm_template = VnfEm(type = 'type1',
                       resource_limit = {'cpu':0.15,
                                         'ram':64*u.MB,
                                         'rom':128*u.MB},
                       rate_per_core = 10)
vnfManager.add_vnf_into_templates(vnfEm_template)
vnfEm_template = VnfEm(type = 'type2',
                       resource_limit = {'cpu':0.15,
                                         'ram':64*u.MB,
                                         'rom':128*u.MB},
                       rate_per_core = 10)
vnfManager.add_vnf_into_templates(vnfEm_template)

# endregion

# region step3: define sfc topologies------------------------------------------------

from netorchestr.envir.applications.ueapp import SfcReq

sfc_req_0 = SfcReq(id=0,
                   start_time=5*u.ms,
                   end_time=350*u.ms,
                   sfc_type='Ue2Ue', 
                   sfc_vnfs_type=["type0","type1","type2"],
                   sfc_vnfs_shared=[True,True,True],
                   sfc_qos={'latency':9*u.ms,'overrate':0.1},
                   sfc_trans_model={'type':'Poisson',
                                    'interval':1*u.ms, 
                                    'payload_size':1*u.kbit},
                   sfc_end_point=[ue_0,ue_1]) 


sfc_req_1 = SfcReq(id=1,
                   start_time=10*u.ms,
                   end_time=800*u.ms,
                   sfc_type='Ue2Ue', 
                   sfc_vnfs_type=["type0","type1"],
                   sfc_vnfs_shared=[True,True],
                   sfc_qos={'latency':6*u.ms,'overrate':0.1},
                   sfc_trans_model={'type':'Poisson',
                                    'interval':1*u.ms, 
                                    'payload_size':1*u.kbit},
                   sfc_end_point=[ue_2,ue_3]) 

sfcReqlist = [sfc_req_0, sfc_req_1]

#  endregion

# region step4: define controller -----------------------------------------------------

from netorchestr.envir.node.controller.mano.trace import TRACE_RESULT, TRACE_NFVI
from netorchestr.envir.node.controller.mano import ControllerMano
from netorchestr.envir.node.controller.mano.solver_deploy import SolverDeploySharedBase, SolverDeploySharedRandom

# 清理TraceVNFFG文件夹
import os
import shutil
if os.path.exists('TraceVNFFG'):
    shutil.rmtree('TraceVNFFG')
    
# SIMULATION_ID = Trace.get_time_stamp()
SIMULATION_ID = 'Test'

TraceResultFile = f'{TRACE_RESULT.__class__.__name__}_{SIMULATION_ID}.csv'
TRACE_RESULT.set(TraceResultFile)
TraceNfviFile = f'{TRACE_NFVI.__class__.__name__}_{SIMULATION_ID}.csv'
TRACE_NFVI.set(TraceNfviFile)

controller = ControllerMano(name='Controller', 
                            net=net, 
                            vnfManager=vnfManager, 
                            sfcReqlist=sfcReqlist)
solver_deploy = SolverDeploySharedRandom(name='SolverDeploy')
controller.set_solver_deploy(solver_deploy)

net.add_module(controller)


# endregion

# region step5: start simulation-------------------------------------------------------

# net.draw_flag = True
# net.draw_time_accuracy = 200*u.ms
# net.draw_region = [station_0.mobiusTraj.init_gps[0]-0.5, 
#                    station_2.mobiusTraj.init_gps[0]+0.5, 
#                    station_0.mobiusTraj.init_gps[1]-0.5, 
#                    station_2.mobiusTraj.init_gps[1]+0.5]
# net.speed_up_for_location = True
net.run(until=1000*u.ms)

# import code
# code.interact(local=locals())

# endregion
